﻿using System.Net.Sockets;
using System.Net;
using System;
using System.Security;
using System.Threading;

namespace IDEAMS.core.opm
{
    /// <summary>
    /// OPM 协议命令字
    /// </summary>
    public enum OPM_CmdID
    {
        CMD_UNKNOWN = -1, //unknown
        CMD_READ_PARAMETER = 0x02, //读参数
        CMD_READ_UPLOAD_CONF = 0x03, // 读取设备上报信息
        CMD_WRITE_LOCAL_ADDR = 0x04, //写本地地址
        CMD_WRITE_UPLOAD_CONF = 0x05,  // 写入设备上报信息
        CMD_WRITE_TIME = 0x06, //写本地时间
        CMD_READ_TIME = 0x07, //读时间
        CMD_READ_STATUS = 0x08, //读取连续发送状态，光谱/波长/强度连续发送帧数
        CMD_START_FLAG = 0x0a, //启停
        CMD_READ_GRATING_WAVE_LEN = 0x0c, //0-31通道光栅的波长数据
        CMD_SENSOR_UPLOAD_START_FLAG = 0x0d, // 启停传感器数据控制
        CMD_READ_SENSOR_UPLOAD = 0x0e, // 设备上报传感器数据
        CMD_WARNNING = 0x14, //报警
        CMD_READ_SPECTRUM = 0x16, //读某个通道（0-31）的光谱数据
        CMD_READ_SPECTRUM_SUMMARY = 0x17, //读波长起始值，波长间隔、数据点数、通道数、返回数据帧数
        CMD_SET_CHANNEL_PARAMETER = 0x18, //设置通道参数
        CMD_READ_CHANNEL_PARAMETER = 0x19, //读取通道参数
        CMD_WRITE_SENSOR_CONF = 0x20, // 设置传感器配置参数
        CMD_READ_SENSOR_CONF_COUNT = 0x21, // 读取传感器个数
        CMD_READ_SENSOR_CONF = 0x22, // 读取传感器配置参数
        CMD_WRITE_SENSOR_CONF_ZERO = 0x23, // 清空传感器配置参数
        CMD_SET_WAVE_OFFSET = 0x24, //设置波长偏移量
        CMD_READ_WAVE_OFFSET = 0x26,//读取波长偏移量
        CMD_SET_MONITOR_FREQ = 0x2e, //设置扫描频率
        CMD_READ_MONITOR_FREQ = 0x30, //读取扫描频率
        CMD_WRITE_HIGHEST_PARAM = 0x32, //写入寻峰参数
        CMD_READ_HIGHEST_PARAM = 0x34, //读取寻峰参数
        CMD_SET_UPLOAD_WAVE_ENABLE = 0x50, // 设置模块上报波长数据使能
        CMD_WRITE_GRATING_CONF = 0x1a, // 写入光栅过滤参数
        CMD_READ_GRATING_CONF = 0x1b, // 读取光栅过滤参数
        CMD_WRITE_GRATING_ZERO = 0x1c, // 写入光栅参数清零
        CMD_READ_GRATING_WAVE_LEN_G = 0xc0, //0-31通道光栅的波长数据
        CMD_READ_GRATING_WAVE_LEN_DB = 0xc1, //0-31通道光栅的波长和强度数据
    }

    static class OPM_SocketError
    {
        public const int SOCKET_RECV_ERR = -1000; //接收错误
        public const int SOCKET_SEND_ERR = -2000; //发送错误
        public const int SOCKET_CLOSED = -9;         //socket 关闭
        public const int SOCKET_UNKNOWN_ERROR = -1; //未知错误
        public const int SOCKET_NO_ERROR = 0; //无错误
    }

    public class OPM_Connector
    {
        private const int RETURN_OK = 0;
        private const int RETURN_NG = -1;
        private const int LOCAL_BIND_ERROR = -2;
        private const int SERVER_BIND_ERROR = -3;

        /// <summary>
        /// 连接状态
        /// </summary>
        private int status = 0;
        private Socket udpSocket;
        private EndPoint remoteEP;
        private const int HEADER_LEN = 2; // header 长度

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="serverIp"></param>
        /// <param name="serverPort"></param>
        /// <returns></returns>
        public int Init(string serverIp, int serverPort)
        {
            int rn = 0;

            if(udpSocket != null)
            {
                udpSocket.Close();
            }
            try
            {
                udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                if(udpSocket == null)
                {
                    rn = LOCAL_BIND_ERROR;
                    goto rtn;
                }

                udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 1024 * 50);
            }
            catch(Exception)
            {
                rn = LOCAL_BIND_ERROR;
                goto rtn;
            }

            try
            {
                remoteEP = new IPEndPoint(IPAddress.Parse(serverIp), serverPort);
            }
            catch(Exception)
            {
                rn = SERVER_BIND_ERROR;
                goto rtn;
            }

        rtn:
            status = (rn == 0 ? 0 : -1);
            return rn;
        }

        /// <summary>
        /// 设置超时时间
        /// </summary>
        /// <param name="msec"></param>
        public void SetRecvTimeOut(int msec)
        {
            // 设置超时时间
            udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, msec);
        }
        /// <summary>
        /// 取消超时时间
        /// </summary>
        public void CancelRecvTimeOut()
        {
            // 设置超时时间
            udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 0);
        }

        /// <summary>
        /// 获取连接状态 0表示连接成功
        /// </summary>
        /// <returns></returns>
        public int GetStatus()
        {
            return status;
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void End()
        {
            if (udpSocket != null)
            {
                udpSocket.Close();
                udpSocket = null;
            }
            status = -1;
        }

        /// <summary>
        /// 向server发送数据
        /// </summary>
        /// <param name="data"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public int SendToServer(byte[] data, int offset, int length)
        {
            int rn = RETURN_OK;
            int tmp_len = 0; // 本次发送的长度

            try
            {
                if (udpSocket == null) return OPM_SocketError.SOCKET_CLOSED;

                tmp_len = udpSocket.SendTo(data, offset, length, SocketFlags.None, remoteEP);
                if (tmp_len != length)
                {
                    // 发送异常
                    rn = OPM_SocketError.SOCKET_SEND_ERR;
                }
            }
            catch (ObjectDisposedException)
            {
                rn = OPM_SocketError.SOCKET_CLOSED;
            }
            catch (ArgumentNullException)
            {
                rn = OPM_SocketError.SOCKET_SEND_ERR;
            }
            catch (ArgumentOutOfRangeException)
            {
                rn = OPM_SocketError.SOCKET_SEND_ERR;
            }
            catch (SocketException)
            {
                rn = OPM_SocketError.SOCKET_SEND_ERR;
            }
            catch (SecurityException)
            {
                rn = OPM_SocketError.SOCKET_SEND_ERR;
            }

            return rn != RETURN_OK ? rn : length;
        }

        /// <summary>
        /// 接收消息
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        public int RecvMsg(ref byte[] buffer, int offset)
        {
            int msglen = 0;
            int tryCounts = 0;

            while (msglen <= 0 && tryCounts++ < 1)
            {
                try
                {
                    IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
                    EndPoint senderRemote = (EndPoint)sender;
                    // 接收数据
                    if (udpSocket == null)
                    {
                        msglen = OPM_SocketError.SOCKET_CLOSED;
                    }
                    msglen = udpSocket.ReceiveFrom(buffer, offset, buffer.Length, SocketFlags.None, ref senderRemote);
                }
                catch (ObjectDisposedException)
                {
                    msglen = OPM_SocketError.SOCKET_CLOSED;
                }
                catch (ArgumentNullException)
                {
                    msglen = OPM_SocketError.SOCKET_RECV_ERR;
                }
                catch (ArgumentOutOfRangeException)
                {
                    msglen = OPM_SocketError.SOCKET_RECV_ERR;
                }
                catch (SocketException)
                {
                    msglen = OPM_SocketError.SOCKET_RECV_ERR;
                }
                catch (Exception)
                {
                    msglen = OPM_SocketError.SOCKET_UNKNOWN_ERROR;
                }

                // 等待一会儿再试
                if (msglen < 0)
                {
                    Thread.Sleep(300);
                }
            }

            return msglen;
        }
    }
}
